package net.alpenviewer.map;
import java.io.File;
import java.io.FileNotFoundException;
import java.text.DecimalFormat;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.geotools.measure.Latitude;
import org.geotools.measure.Longitude;


/**
 * Representing an SRTM (*.hgt) data.
 */
public final class SRTM {
	
	public static final String SUPPORTED_FILE_EXT = "hgt";
	public static final String SUPPORTED_FILE_PATTERN = "^([NS])(\\d\\d)([EW])(\\d\\d\\d)\\." + SUPPORTED_FILE_EXT + "$";
	
	private static final DecimalFormat latFormat = new DecimalFormat("00");
	private static final DecimalFormat lonFormat = new DecimalFormat("000");
	
	/** n, s, e, w */
	public final int[] extentNSEW;
	/** nw, n, ne, w, e, sw, s, se */
	public final String[] neighborNames;
	/** the lat/long coordination of the south-west boundary. */
	public final LatLong southWest;
	/** the lat/long coordination of the north-east boundary. */
	public final LatLong northEast;
	/** SRTM resolution in arc seconds. */
	public final int arcSecond;
	/** SRTM file source */
	public final File src; // row major, north to south, west to east.	
	
	/**
	 * Construct a SRTM from a file.
	 * @param f source file
	 */
	public SRTM(final File f) {

		/* file name -> extent, neighbors name */ 
		{
			final Pattern fpattern = Pattern.compile(SUPPORTED_FILE_PATTERN);
			final Matcher match = fpattern.matcher(f.getName()); 

			if (!match.matches()) {
				throw new IllegalArgumentException("Invalid SRTM file name against the pattern " + fpattern.pattern());
			} else {
				final int towardNorth = match.group(1).equalsIgnoreCase("N")? 1 : -1;
				final int towardEast  = match.group(3).equalsIgnoreCase("E")? 1 : -1;
				final int _yAngle = Integer.parseInt( match.group(2) );
				final int _xAngle = Integer.parseInt( match.group(4) );
				if (_yAngle > 90 || _xAngle > 180) {
					throw new IllegalArgumentException("Invalid SRTM file name with invalid angle.");				
				}

				{ // extent
					this.extentNSEW = new int[] {
						(_yAngle + towardNorth) * towardNorth,
						 _yAngle                * towardNorth,
						(_xAngle + towardEast ) * towardEast,
						 _xAngle                * towardEast
					};
					this.southWest = new LatLong(
							new Latitude ( this.extentNSEW[1] ), 
							new Longitude( this.extentNSEW[3] ));
					this.northEast = new LatLong(
							new Latitude ( this.extentNSEW[0] ), 
							new Longitude( this.extentNSEW[2] ));
					//System.out.println("Extent: " + this.southWest + "-" + this.northEast);
				}
				
				{ // neighbors name
					final int s = extentNSEW[1];
					final int w = extentNSEW[3];
					this.neighborNames = new String[]{
					((towardNorth>0 || _yAngle==1)?"N":"S") + (latFormat.format(Math.abs(s+1))) + ((towardEast<0 || _xAngle==0)?"W":"E") + (lonFormat.format(Math.abs(w-1))), 
					((towardNorth>0 || _yAngle==1)?"N":"S") + (latFormat.format(Math.abs(s+1))) + ((towardEast>0)              ?"E":"W") + (lonFormat.format(Math.abs( w ))),
					((towardNorth>0 || _yAngle==1)?"N":"S") + (latFormat.format(Math.abs(s+1))) + ((towardEast>0 || _xAngle==1)?"E":"W") + (lonFormat.format(Math.abs(w+1))),
					((towardNorth>0)              ?"N":"S") + (latFormat.format(Math.abs( s ))) + ((towardEast<0 || _xAngle==0)?"W":"E") + (lonFormat.format(Math.abs(w-1))),
					((towardNorth>0)              ?"N":"S") + (latFormat.format(Math.abs( s ))) + ((towardEast>0 || _xAngle==1)?"E":"W") + (lonFormat.format(Math.abs(w+1))),
					((towardNorth<0 || _yAngle==0)?"S":"N") + (latFormat.format(Math.abs(s-1))) + ((towardEast<0 || _xAngle==0)?"W":"E") + (lonFormat.format(Math.abs(w-1))),
					((towardNorth<0 || _yAngle==0)?"S":"N") + (latFormat.format(Math.abs(s-1))) + ((towardEast>0)              ?"E":"W") + (lonFormat.format(Math.abs( w ))),
					((towardNorth<0 || _yAngle==0)?"S":"N") + (latFormat.format(Math.abs(s-1))) + ((towardEast>0 || _xAngle==1)?"E":"W") + (lonFormat.format(Math.abs(w+1)))};					
				}				
			}
			
		}

		/* file exists? */
		{
			if (!f.exists()) {
				throw new IllegalArgumentException(new FileNotFoundException(f.getAbsolutePath()));
			}
			this.src = f;
		}

		/* file size */
		{
			final long flength = f.length(); 
			if (flength == 3601*3601*2) {
				this.arcSecond = 1;
			} else if (flength == 1201*1201*2) {
				this.arcSecond = 3;
			} else {
				throw new IllegalArgumentException("Invalid file size.");	
			}
		}		
				
	}	
	
	@Override public String toString() {
		return src.getName().substring(0, src.getName().indexOf('.'));
	}
	
	/**
	 * Find and load all possible neighbors of the given SRTM.
	 *  +----+-----+----+
	 *  | NW |  N  | NE |
	 *  |  W |     | E  |
	 *  | SW |  S  | SE |
	 *  +----+-----+----+
	 * If a SRTM file is not found in the same directory, then a Null value is returned. 
	 * @return an array of all SRTM neighbors. In the order of nw, n, ne, w, e, sw, s and se.
	 */
	public static SRTM[] getNeighborSrtm(SRTM srtm) {

		SRTM[] neighborSrtms = new SRTM[srtm.neighborNames.length];

		String parentDir = srtm.src.getParent();
		if (parentDir == null) {
			parentDir = ".";
		}
		for (int i=0; i<srtm.neighborNames.length; i++){
			File srtmf = new File(parentDir + File.separator + srtm.neighborNames[i] + "." + SRTM.SUPPORTED_FILE_EXT);
			if (srtmf.exists()) {
				neighborSrtms[i] = new SRTM(srtmf);
				if (neighborSrtms[i].arcSecond != srtm.arcSecond) {
					throw new RuntimeException("The neighbor has different resolution.");
				}
			} else {
				neighborSrtms[i] = null;	
			}
		}
		return neighborSrtms;
	}
}

